package com.vains.db.util;

import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.vains.constant.db.TemplateSuffixConstant;
import com.vains.db.model.GeneratorModel;
import com.vains.db.model.GeneratorRes;
import com.vains.db.model.TableInfos;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * 代码生成帮助类
 *
 * @author vains
 * @version 1.0
 * @since 2020/6/3 17:52
 **/
@Slf4j
public class CodeGeneratorUtils {

    private CodeGeneratorUtils(){}

    public static List<List<GeneratorRes>> generator(GeneratorModel model) {
        // 代码生成器
        MyAutoGenerator mpg = new MyAutoGenerator();
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir(model.getOutputPath() + "/java");
        gc.setAuthor(model.getAuthor());
        gc.setOpen(false);
        //实体属性 Swagger2 注解
        gc.setSwagger2(model.getSwagger2());
        mpg.setGlobalConfig(gc);
        // 数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl(model.getDataBaseUrl());
        dsc.setDriverName(model.getDataBaseDriver());
        dsc.setUsername(model.getDataBaseUsername());
        dsc.setPassword(model.getDataBasePassword());
        mpg.setDataSource(dsc);
        // 包配置
        PackageConfig pc = new PackageConfig();
        pc.setModuleName(model.getModuleName());
        pc.setParent(model.getParentPackage());
        mpg.setPackageInfo(pc);

        // 自定义配置
        InjectionConfig cfg = new InjectionConfig() {
            @Override
            public void initMap() {
                // to do nothing
            }
        };

        // xml模板
        String xmlTemplatePath = "/templates/mapper.xml.ftl";

        // 自定义输出配置
        List<FileOutConfig> focList = new ArrayList<>();
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(xmlTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                return model.getOutputPath() + "/resources/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
            }
        });

        // js 模板
        String jsTemplatePath = "/templates/entity.js.ftl";
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(jsTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                return model.getOutputPath() + "/resources/static/js/" + pc.getModuleName()
                        + "/" + tableInfo.getEntityName() + TemplateSuffixConstant.ENTITY_JS;
            }
        });

        // html 模板
        String htmlTemplatePath = "/templates/entity.html.ftl";
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(htmlTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                return model.getOutputPath() + "/resources/static/page/"
                        + "/" + tableInfo.getEntityName() + TemplateSuffixConstant.ENTITY_HTML;
            }
        });

        // html 模板
        String cssTemplatePath = "/templates/entity.css.ftl";
        // 自定义配置会被优先输出
        focList.add(new FileOutConfig(cssTemplatePath) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 自定义输出文件名 ， 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化！！
                return model.getOutputPath() + "/resources/static/page/"
                        + "/" + tableInfo.getEntityName() + TemplateSuffixConstant.ENTITY_CSS;
            }
        });
        cfg.setFileOutConfigList(focList);
        mpg.setCfg(cfg);

        // 配置模板
        TemplateConfig templateConfig = new TemplateConfig();

        templateConfig.setXml(null);
        mpg.setTemplate(templateConfig);

        // 策略配置
        StrategyConfig strategy = new StrategyConfig();
        strategy.setNaming(NamingStrategy.underline_to_camel);
        strategy.setColumnNaming(NamingStrategy.underline_to_camel);
        strategy.setEntityLombokModel(model.getEntityLombokModel());
        strategy.setRestControllerStyle(model.getRestControllerStyle());
        // 公共父类
        strategy.setSuperControllerClass(model.getSuperControllerClass());
        // 写于父类中的公共字段
        strategy.setSuperEntityColumns("id");
        strategy.setInclude(model.getTableNames());
        strategy.setControllerMappingHyphenStyle(model.getControllerMappingHyphenStyle());
        strategy.setTablePrefix(pc.getModuleName() + "_");
        mpg.setStrategy(strategy);
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        if (Boolean.TRUE.equals(model.getPreview())) {
            return mpg.processTemplateToString();
        }
        mpg.execute();
        return Collections.emptyList();
    }

    public static List<TableInfos> getTableInfo(GeneratorModel model) {
        DataSourceConfig config = new DataSourceConfig();
        config.setUrl(model.getDataBaseUrl());
        config.setDriverName(model.getDataBaseDriver());
        config.setUsername(model.getDataBaseUsername());
        config.setPassword(model.getDataBasePassword());
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try (Connection connection = config.getConn()) {
            List<TableInfos> tableInfos = new ArrayList<>();
            // 获取当前数据库名称
            String dataBaseName = connection.getCatalog();
            ResultSet tables = connection.getMetaData().getTables(dataBaseName, "%", "%", new String[]{"TABLE"});
            while (tables.next()) {
                TableInfos info = new TableInfos();
                String tableName = tables.getString("TABLE_NAME");
                info.setTableName(tableName);
                info.setDataBaseName(dataBaseName);
                String sql = "show table status from " + dataBaseName + " like '" + tableName + "'";
                log.info("执行sql：{}", sql);
                try (ResultSet set = connection.createStatement().executeQuery(sql)) {
                    if (set.next()) {
                        if (StringUtils.isNotBlank(set.getString("CREATE_TIME"))) {
                            info.setCreateTime(dateFormat.parse(set.getString("CREATE_TIME")));
                        }
                        if (StringUtils.isNotBlank(set.getString("UPDATE_TIME"))) {
                            info.setUpdateTime(dateFormat.parse(set.getString("UPDATE_TIME")));
                        }
                        info.setRemarks(set.getString("COMMENT"));
                    }
                } catch (SQLException e) {
                    log.error("数据库连接失败！", e);
                    return Collections.emptyList();
                } catch (ParseException e) {
                    log.error("时间格式异常！", e);
                    return Collections.emptyList();
                }
                tableInfos.add(info);
            }
            return tableInfos;
        } catch (SQLException e) {
            log.error("数据库连接失败！", e);
            return Collections.emptyList();
        }
    }

    /**
     * 根据数据库连接信息获取一个代码生成工具类
     * @param driverClassName 驱动
     * @param password 密码
     * @param username 用户名
     * @param url url
     * @return 代码生成工具类
     */
    public static GeneratorModel setDataBaseInfoForModel(String driverClassName, String password, String username, String url) {
        GeneratorModel model = new GeneratorModel();
        model.setDataBaseDriver(driverClassName);
        model.setDataBasePassword(password);
        model.setDataBaseUsername(username);
        model.setDataBaseUrl(url);
        return model;
    }

}
